#ifndef __CARRAY_H
#define __CARRAY_H
#include "stdtype.h"
#include "dlist.h"
#include "general.h"
#include "assert.h"

/**
 * this function will get the next node
 * @prototype: mx_carray_node_pt mx_carray_node_next(mx_carray_node_pt);
 * @note: make sure that the specified node in an array
 */
#define mx_carray_node_next(node) ({ \
    assert_true((node)->owner != mx_null);    \
    mx_carray_node_pt ret;   \
    if ((node)->dlist.next == &((node)->owner->dlist)) {   \
        ret = container((node)->owner->dlist.next, mx_carray_node_t, dlist);  \
    } else {    \
        ret = container((node)->dlist.next, mx_carray_node_t, dlist); \
    }   \
    ret;    \
})

/**
 * this function will get the prev node
 * @prototype: mx_carray_node_pt mx_carray_node_prev(mx_carray_node_pt);
 * @note: make sure that the specified node in an array
 */
#define mx_carray_node_prev(node) ({ \
    assert_true((node)->owner != mx_null);    \
    mx_carray_node_pt ret;   \
    if ((node)->dlist.prev == &((node)->owner->dlist)) {   \
        ret = container((node)->owner->dlist.prev, mx_carray_node_t, dlist);  \
    } else {    \
        ret = container((node)->dlist.prev, mx_carray_node_t, dlist); \
    }   \
    ret;    \
})

/**
 * this function will iterate the specified array (order, ->next)
 * @prototype: void mx_carray_foreach (mx_carray_pt, <node>, {...});
 * @note: do not change the struct of double list, such as remove or insert nodes
 */
#define mx_carray_foreach(array, node, ...) {    \
    for (mx_dlist_pt next = ((array)->dlist).next; next != &(array)->dlist; next = next->next) {   \
        mx_carray_node_pt node = container(next, mx_carray_node_t, dlist);    \
        __VA_ARGS__ \
    }   \
}

/**
 * this function will iterate the specified array (reverse, ->prev)
 * @prototype: void mx_carray_foreach_prev (mx_carray_pt, <node>, {...});
 * @note: do not change the struct of double list, such as remove or insert nodes
 */
#define mx_carray_foreach_prev(array, node, ...) {    \
    for (mx_dlist_pt prev = ((array)->dlist).prev; prev != &(array)->dlist; prev = prev->prev) {   \
        mx_carray_node_pt node = container(prev, mx_carray_node_t, dlist);    \
        __VA_ARGS__ \
    }   \
}

/**
 * get the first node of array
 */
#define mx_carray_firstnode(array) (mx_dlist_next(array) == (mx_dlist_pt)array ? mx_null : (mx_carray_node_pt)mx_dlist_next(array))

/**
 * get the last node of array
 */
#define mx_carray_lastnode(array) (mx_dlist_next(array) == (mx_dlist_pt)array ? mx_null : (mx_carray_node_pt)mx_dlist_prev(array))

/**
 * check whether the spcified node is the first in array
 */
#define mx_carray_node_isfirst(node) (mx_dlist_prev(node) == (mx_dlist_pt)((node)->owner))

/**
 * check whether the spcified node is the last in array
 */
#define mx_carray_node_islast(node) (mx_dlist_next(node) == (mx_dlist_pt)((node)->owner))

/**
 * struct chaotic array definition
 */
typedef struct
{
    mx_dlist_t dlist;
    mx_size_t count;
} mx_carray_t, *mx_carray_pt;

/**
 * struct chaotic array node definition
 */
typedef struct
{
    mx_dlist_t dlist;       // putting it in the first place can directly calcuate the start-address of the host by dlist with the "mx_container" macro,
                            // which contain the mx_carray_node_t
    mx_carray_pt owner;     // affiliated array
} mx_carray_node_t, *mx_carray_node_pt;

/* initialize the specified array */
void mx_carray_init (mx_carray_pt parray);
/* initialize the specified array node */
void mx_carray_node_init (mx_carray_node_pt pnode);
/* remove the specified array node from array which it is affiliated */
void mx_carray_remove (mx_carray_node_pt pnode);
/* add an array node to the specified array's last */
void mx_carray_append (mx_carray_pt parray, mx_carray_node_pt pnode);
/* add an array node to the specified array's first */
void mx_carray_preppend (mx_carray_pt parray, mx_carray_node_pt pnode);
/* get an array node from array by index */
mx_carray_node_pt mx_carray_get (mx_carray_pt parray, mx_usize_t index);
/* get the index of the specified array node in his host */
mx_size_t mx_carray_index (mx_carray_node_pt pnode);
/* get the count of the specified array */
mx_size_t mx_carray_count (mx_carray_pt parray);
/* check whether the specified node contain in the array */
mx_bool_t mx_carray_iscontain (mx_carray_pt parray, mx_carray_node_pt pnode);
/* insert an node before the specified node */
mx_bool_t mx_carray_node_insert_before (mx_carray_node_pt tnode, mx_carray_node_pt node);
/* insert an node after the specified node */
mx_bool_t mx_carray_node_insert_after (mx_carray_node_pt tnode, mx_carray_node_pt node);

#endif
